package analiseSintatica.Regras;

import java.util.ArrayList;
import java.util.LinkedList;

import AnaliseLexicaFinal.EstruturaDeDados.LinkToken;
import analiseSemantica.ErroSemantico;
import analiseSemantica.Tipos.TiposSemanticos;
import analiseSemantica.estruturadedados.Escopo;
import analiseSintatica.ErroSintaticoException;
import analiseSintatica.estruturadados.NoArvore;

/**
 * Contem o esqueleto para a implementacao das regras para a analise sintatica
 * @author amferraz
 * @author Elisson
 */
public abstract class Regra{

    protected LinkedList<String> primeiro = null;
//    protected Escopo<String, Token> escopoatual;

    /**
     * Instancie sua Regra passando a lista de tokens atual. Quando voce mandar
     * analisar, essa lista de tokens serah consumida atraves do metodo
     * <code>analisar()</code>. O metodo <code>analisar()</code> tambem retorna
     * o noh da arvore correspondente aa regra analisada.
     *
     * @param listaTokens a lista de tokens do codigo
     */




    /**
     * Realiza analise sintatica correspondente ao nome da classe
     * @param listaTokens lista atual de tokens do codigo ainda nao analisada
     * @return - NoArvore o noh correspondente a essa regra
     * @throws Exception caso nao seja reconhecido o token nessa regra.
     */
    public abstract TiposSemanticos analisar(LinkedList<LinkToken> listaTokens) throws ErroSintaticoException;



    /**
     * Realiza um operacao de uniao entre duas listas
     * @param lista1
     * @param lista2
     * @return Lista com a uniao
     */
    public static LinkedList<String> getUniao(LinkedList<String> lista1, LinkedList<String> lista2) {

        for (int i = 0; i < lista2.size(); i++) {
            if (!lista1.contains(lista2.get(i))) {
                lista1.add(lista2.get(i));
            }
        }
        return lista1;
    }

    public static LinkedList<String> getUniao(LinkedList<String> lista, String nova) {
        if (!lista.contains(nova)) {
            lista.add(nova);
        }
        return lista;
    }

    /**
     * Reconhece terminal a partir de um token
     * @param listatokens - Tokens do codigo
     * @param token - Token que deve ser reconhecido
     * @return - Sub-arvore sintatica
     * @throws ErroSintaticoException
     */
    public NoArvore reconheceToken(LinkedList<LinkToken> listatokens, String token, String estrutura) throws ErroSintaticoException {
    	if (listatokens.getFirst().getToken().equals(token)) {
            listatokens.removeFirst();
            return new NoArvore(token, true);
        } else {
            ErroSintaticoException erro = new ErroSintaticoException();
            erro.addErros("Falta " + token+" antes do token "+listatokens.getFirst().getLexema(), estrutura, listatokens.getFirst().getLinha());
            throw erro;
        }
    }


    public static String isPonteiro(String tipo){
    	if((tipo.length() > 9) && tipo.substring(0, 9).equals("ponteiro."))
    		return tipo.substring(9, tipo.length());
    	return null;
    }
    
    public static String isEndereco(String tipo){
    	if((tipo.length() > 9) && tipo.substring(0, 9).equals("endereco."))
    		return tipo.substring(9, tipo.length());
    	return null;
    }
    
    public static boolean isTipoPrimitivo(String tipo){
    	if(tipo.equals("int") || tipo.equals("char") || tipo.equals("boolean") || tipo.equals("float"))
    		return true;
    	return false;
    }
    
    public LinkToken reconheceRetornaToken(LinkedList<LinkToken> listatokens, String token, String estrutura) throws ErroSintaticoException {
    	if (listatokens.getFirst().getToken().equals(token)) {
            return listatokens.removeFirst();
//            return new NoArvore(token, true);
        } else {
            ErroSintaticoException erro = new ErroSintaticoException();
            erro.addErros("Falta " + token+" antes do token "+listatokens.getFirst().getLexema(), estrutura, listatokens.getFirst().getLinha());
            throw erro;
        }
    }

    /**
     * Reconhece terminal a partir de um lexema
     * @param listatokens - Tokens do codigo
     * @param token - Token desejado
     * @param lexema - Lexema que deve ser reconhecido
     * @return - Sub-arvore sintatica
     * @throws ErroSintaticoException
     */
    public NoArvore reconheceLexema(LinkedList<LinkToken> listatokens, String token, String lexema, String estrutura) throws ErroSintaticoException {
        if (listatokens.getFirst().getToken().equals(token) && listatokens.getFirst().getLexema().equals(lexema)) {
            listatokens.removeFirst();
            return new NoArvore(token, true);
        } else {
            ErroSintaticoException erro = new ErroSintaticoException();
            erro.addErros("Falta " + lexema + " antes do token "+listatokens.getFirst().getLexema(), estrutura, listatokens.getFirst().getLinha());
            throw erro;
        }
    }

    public LinkToken reconheceRetornaLexema(LinkedList<LinkToken> listatokens, String token, String lexema, String estrutura) throws ErroSintaticoException {
        if (listatokens.getFirst().getToken().equals(token) && listatokens.getFirst().getLexema().equals(lexema)) {
            return listatokens.removeFirst();
//            return new NoArvore(token, true);
        } else {
            ErroSintaticoException erro = new ErroSintaticoException();
            erro.addErros("Falta " + lexema + " antes do token "+listatokens.getFirst().getLexema(), estrutura, listatokens.getFirst().getLinha());
            throw erro;
        }
    }

    /**
     * Verifica se determinado token existe em um conjunto
     * @param conjunto - Conjunto de Strings
     * @param token
     * @return
     */
    public boolean listaContem(LinkedList<String> conjunto, LinkToken token) {
        if ((token.getToken().equals("operador_logico")) || (token.getToken().equals("operador_igualdade")) ||
                (token.getToken().equals("operador_relacional")) || (token.getToken().equals("operador_adicao")) ||
                (token.getToken().equals("operador_multiplicacao")) || (token.getToken().equals("incremento")) ||
                (token.getToken().equals("delimitador"))) {
            return conjunto.contains(token.getLexema());
        } else {
            return conjunto.contains(token.getToken());
        }
    }




    /**
     * Adicionar aa lista <code>primeiro</code> os elementos primeiro da regra
     */
    protected abstract void setPrimeiro();



    /**
     * Funcao primeiro para cada nao-terminal
     * @return Lista contendo os terminais que estao na funcao primeiro
     */
    public LinkedList<String> getPrimeiro() {
        this.primeiro = new LinkedList<String>();
        this.setPrimeiro();

        return this.primeiro;
    }

}
